package set

import (
	"asd/bintree"
	"fmt"
)

type Set bintree.BinTree

type setPair struct {
	From *Set
	To *Set
}

func NewSet() *Set {
	t := new(Set)
	((*bintree.BinTree)(t)).ForceLocked()
	((*bintree.BinTree)(t)).Duplicate(false)
	return t
}

func (s *Set)String() string {	
	t := ((*bintree.BinTree)(s)) 
	ret:= fmt.Sprintf("Set(%p):%d", t.Root(), t.Size())
	return ret
}


func (s *Set)Insert(i bintree.Comparable) (bool, error) {
	t := ((*bintree.BinTree)(s))
	return t.InsertSorted(i)
}

func (s *Set)Remove(i bintree.Comparable) (bool, error) {
	t := ((*bintree.BinTree)(s))
	elem, err := t.Search(i)
	if elem==nil {
		return false, err
	}
	return t.Remove(elem)
}

func (s *Set)Contains(i bintree.Comparable) bool {	
	t := ((*bintree.BinTree)(s))
	elem, _ := t.Search(i)
	return (elem != nil)
}

func unionSupport(n *bintree.BTNode, uData interface{}) bool {
	t := uData.(*bintree.BinTree)
	t.InsertSorted(n.Value.(bintree.Comparable))
	return true
}

func (s *Set)Union(x *Set) *Set {
	t := ((*bintree.BinTree)(s))
	k := ((*bintree.BinTree)(x))
	z := NewSet()
	z1 := ((*bintree.BinTree)(z))
	t.Root().Walk(unionSupport, z1)
	k.Root().Walk(unionSupport, z1)
	return z
}

func intersectionSupport(n *bintree.BTNode, uData interface{}) bool {
	f := (*bintree.BinTree)(uData.(*setPair).From)
	t := (*bintree.BinTree)(uData.(*setPair).To)
	if res, _ := f.Search(n.Value.(bintree.Comparable)); res!=nil {
		t.InsertSorted(n.Value.(bintree.Comparable))
	}
	return true
}

func (s *Set)Intersection(x *Set) *Set {
	t := ((*bintree.BinTree)(s))
	k := ((*bintree.BinTree)(x))
	z := NewSet()
	if k.Size() < t.Size() {
		k.Root().Walk(intersectionSupport, &setPair{From:s,To:z})
	} else {
		t.Root().Walk(intersectionSupport, &setPair{From:x,To:z})
	}
	return z
}

func differenceSupport(n *bintree.BTNode, uData interface{}) bool {
	f := (*bintree.BinTree)(uData.(*setPair).From)
	t := (*bintree.BinTree)(uData.(*setPair).To)
	if res, _ := f.Search(n.Value.(bintree.Comparable)); res==nil {
		t.InsertSorted(n.Value.(bintree.Comparable))
	}
	return true
}

func (s *Set)Difference(x *Set) *Set {
	t := ((*bintree.BinTree)(s))
	z := NewSet()
	t.Root().Walk(differenceSupport, &setPair{From:x, To:z})	
	return z
}